package com.tssup.fesco.service.impl;

import com.tssup.fesco.common.enums.YesNoBoolean;
import com.tssup.fesco.common.queue.IQueue;
import com.tssup.fesco.common.queue.QueueException;
import com.tssup.fesco.common.queue.QueueMessage;
import com.tssup.fesco.common.enumeration.OrderAppStatus;
import com.tssup.fesco.common.constants.OfcConstants;
import com.tssup.fesco.common.enumeration.InvoiceStatus;
import com.tssup.fesco.common.enumeration.LogisticsStatus;
import com.tssup.fesco.common.enumeration.OfcOrderStatus;
import com.tssup.fesco.common.enumeration.OrderStatus;
import com.tssup.fesco.common.utils.DateUtils;
import com.tssup.fesco.sales.goods.persistent.mapper.LSalesVolumeMapper;
import com.tssup.fesco.sales.goods.persistent.pojo.LSalesVolume;
import com.tssup.fesco.sales.order.persistent.mapper.IOrderDetailMapper;
import com.tssup.fesco.sales.order.persistent.pojo.SkuStatistics;
import com.tssup.fesco.service.*;
import com.tssup.fesco.ofc.vo.OrderQueueMessage;
import com.tssup.fesco.sales.ofc.persistent.mapper.OfcIOrderInfoMapper;
import com.tssup.fesco.sales.ofc.persistent.pojo.OfcIOrderInfo;
import com.tssup.fesco.sales.ofc.persistent.pojo.OfcRStaticData;
import com.tssup.fesco.sales.order.persistent.mapper.IOrderLogisticsMapper;
import com.tssup.fesco.sales.order.persistent.mapper.IOrderMapper;
import com.tssup.fesco.sales.order.persistent.pojo.IOrder;
import com.tssup.fesco.sales.rule.persistent.mapper.RStaticDataMapper;
import com.tssup.fesco.sales.rule.persistent.pojo.RStaticData;
import com.tssup.fesco.sales.user.persistent.mapper.IInvoiceLogisticsMapper;
import com.tssup.fesco.sales.user.persistent.mapper.IInvoiceMapper;
import com.tssup.fesco.sales.user.persistent.pojo.IInvoice;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * Created by haomin on 2017/4/7.
 */
@Service
public class SchedulServiceImpl implements SchedulService {
    private Logger logger =  LoggerFactory.getLogger(this.getClass());

    //每次生产消息的默认条数
    public static final int messageNum = 50;

    //将超过时限的已审核订单加入到队列的默认时间，单位为分钟
    public static final int minutes = 30;

    //将超过时限的已发货订单自动收货的默认时间，单位为天
    public static final int days = 7;

    public static final int receiveOrderNum = 50;

    @Autowired
    private IOrderMapper orderMapper;

    @Autowired
    private IOrderLogisticsMapper orderLogisticsMapper;

    @Autowired
    private IInvoiceMapper invoiceMapper;

    @Autowired
    private IInvoiceLogisticsMapper invoiceLogisticsMapper;

    @Autowired
    private OfcIOrderInfoMapper ofcIOrderInfoMapper;

    @Autowired
    private RStaticDataMapper staticDataMapper;

    @Autowired
    private OfcOrderLogService orderLogService;

    @Autowired
    private OfcStaticDataService staticDataService;

    @Autowired
    private OrderStatusService orderStatusService;

    @Autowired
    private B2cService b2cOrderInfoService;

    @Autowired
    private IQueue queue;

    @Autowired
    private LSalesVolumeMapper salesVolumeMapper;

    @Autowired
    private IOrderDetailMapper orderDetailMapper;

    @Override
    @Transactional
    public void signInOrder() throws Exception{
        try {
            int daysValue = getStaticData(days, OfcConstants.STATIC_DATA_CODE.OFC_PARAM,OfcConstants.STATIC_DATA_KEY.OFC_WAIT_SIGNIN_TIME );
            //处理销售订单
            List<IOrder> orders = orderMapper.selectByAppStatusAndTime(OrderAppStatus.SENDING.getValue(), null, null, daysValue);
            List<Integer> orderIds = new ArrayList<>();
            List<String> orderCodes = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(orders)){
                orders.forEach(order -> {
                    order.setLogisticsStatus(LogisticsStatus.RECEIVED.getValue());
                    order.setAppStatus(orderStatusService.getAppStatus(order).getValue());
                    orderIds.add(order.getOrderId());
                    if( !OrderStatus.OFC_SPLIT_ORDER.getValue().equals(order.getOrderStatus())){
                        orderCodes.add(order.getOrderCode());
                    }
                });
                //修改订单状态信息
                orderMapper.batchUpdateOrders(orders);
                //修改订单收货时间信息
                orderLogisticsMapper.batchUpdateOrdersReceiveDate(orderIds);
                //修改订单ofc状态信息
                if(CollectionUtils.isNotEmpty(orderCodes)){
                    ofcIOrderInfoMapper.batchUpdateOrders2SignIn(orderCodes);
                    //保存ofc日志记录
                    orderLogService.batchSaveOrderTraceingInfo(orderCodes, OfcOrderStatus.SIGN_IN.getValue(), OfcConstants.SYSTEM_OPERATOR.NAME, OfcOrderStatus.SIGN_IN.getName());
                    orderLogService.batchSaveSysLog(null, orderCodes, OfcConstants.SYSTEM_LOG_MESSAGE.SIGN_IN, null, YesNoBoolean.NO.getValue());
                }
            }

            //处理发票订单
            List<IInvoice> invoices = invoiceMapper.selectByStatusAndTime(InvoiceStatus.SENT.getValue(), null, null, daysValue);
            List<Integer> invoiceIds = new ArrayList<>();
            List<String> invoiceCodes = new ArrayList<>();
            if(CollectionUtils.isNotEmpty(invoices)){
                invoices.forEach(invoice -> {
                    invoice.setStatus(InvoiceStatus.RECEIVE.getValue());
                    invoiceIds.add(invoice.getInvoiceId());
                    invoiceCodes.add(invoice.getInvoiceCode());
                });
                //修改订单状态信息
                invoiceMapper.batchUpdate(invoices);
                //修改订单收货时间信息
                invoiceLogisticsMapper.batchUpdateOrdersReceiveDate(invoiceIds);
                //修改订单ofc状态信息
                ofcIOrderInfoMapper.batchUpdateOrders2SignIn(invoiceCodes);
                //保存ofc日志记录
                orderLogService.batchSaveOrderTraceingInfo(invoiceCodes, OfcOrderStatus.SIGN_IN.getValue(), OfcConstants.SYSTEM_OPERATOR.NAME, OfcOrderStatus.SIGN_IN.getName());
                orderLogService.batchSaveSysLog(null, invoiceCodes, OfcConstants.SYSTEM_LOG_MESSAGE.SIGN_IN, null, YesNoBoolean.NO.getValue());
            }
        } catch ( Exception e ) {
            logger.error(e.getMessage());
            throw e;
        }
    }

    @Override
    @Transactional
    public void createOfcOrderProcess() throws Exception{
        try{
            int receiveOrderNumValue = getStaticData(receiveOrderNum, OfcConstants.STATIC_DATA_CODE.OFC_PARAM, OfcConstants.STATIC_DATA_KEY.RECEIVE_ORDER_NUM);
            //查询数据
            logger.info("开始导入订单到ofc");
            b2cOrderInfoService.sync2Ofc(receiveOrderNumValue);
            logger.info("导入订单到ofc结束");
        }catch( Exception ex ){
            logger.error("ofc订单初始化失败出错",ex);
            throw ex;
        }
    }

    @Override
    public void productMessageProcess() throws Exception{
        logger.info("开始生产审核订单消息到队列");
        try {
            //当现有队列为空时，往队列中生产新消息
            if(queue.ifEmpty(OfcConstants.QUEUE_NAME.AUDIT)){
                int messageNumValue = getStaticData(messageNum, OfcConstants.STATIC_DATA_CODE.OFC_PARAM, OfcConstants.STATIC_DATA_KEY.OFC_QUEUE_MESSAGE_NUM);
                List<OfcIOrderInfo> ofcIOrderInfos = Optional.ofNullable(ofcIOrderInfoMapper.selectByOrderStatus(OfcOrderStatus.OFC_ORDER_INIT.getValue(), messageNumValue, null))
                        .orElse(new ArrayList<>());
                ofcIOrderInfos.forEach(ofcIOrderInfo -> {
                    try {
                        OrderQueueMessage message = new OrderQueueMessage();
                        message.setOrderId(ofcIOrderInfo.getOrderId());
                        queue.sendMessage(OfcConstants.QUEUE_NAME.AUDIT, new QueueMessage(message));
                    } catch ( QueueException e ) {
                        logger.error("OFC订单号为："+ofcIOrderInfo.getOrderId()+"， 生产审核消息失败");
                    }
                });
            }
        }catch ( Exception e ){
            logger.error("生产消息失败", e);
        }
        logger.info("生产审核订单消息到队列结束");
    }

    @Override
    public void productSplitMessage() throws Exception {
        //当现有队列为空时，往队列中生产新消息
        if(queue.ifEmpty(OfcConstants.QUEUE_NAME.SPLIT)) {
            int messageNumValue = getStaticData(messageNum, OfcConstants.STATIC_DATA_CODE.OFC_PARAM, OfcConstants.STATIC_DATA_KEY.OFC_QUEUE_MESSAGE_NUM);
            int minutesValue = getStaticData(minutes, OfcConstants.STATIC_DATA_CODE.OFC_PARAM, OfcConstants.STATIC_DATA_KEY.OFC_WAIT_SPLIT_TIME);

            List<OfcIOrderInfo> ofcIOrderInfos = Optional.ofNullable(
                    ofcIOrderInfoMapper.selectByOrderStatusAndTime(OfcOrderStatus.AUDIT.getValue(), messageNumValue, minutesValue, null))
                    .orElse(new ArrayList<>());
            ofcIOrderInfos.forEach(ofcIOrderInfo -> {
                try {
                    OrderQueueMessage message = new OrderQueueMessage();
                    message.setOrderId(ofcIOrderInfo.getOrderId());
                    queue.sendMessage(OfcConstants.QUEUE_NAME.SPLIT, new QueueMessage(message));
                } catch ( QueueException e ) {
                    logger.error("OFC订单号为：" + ofcIOrderInfo.getOrderId() + "， 生产拆单消息失败");
                }
            });
        }
    }

    @Override
    @Transactional
    public void autoCancelOrder() throws Exception {
        logger.info("【Order Auto Cancel】 ===== Start");
        Optional<RStaticData> staticDataOpt = Optional.ofNullable(staticDataMapper.selectOneByParam("ORDER_CONFIG","AUTO_CANCEL_TIME"));
        Long time = Long.valueOf(staticDataOpt.map(RStaticData::getStaticValue).orElse("0"));
        List<IOrder> orders = orderMapper.selectCancelOrder();
        LocalDateTime now = LocalDateTime.now();
        if (CollectionUtils.isNotEmpty(orders)){
            List<IOrder> cancelList = new ArrayList<>();
            ZoneId defaultZoneId = ZoneId.systemDefault();
            orders.stream().forEach(order ->{
                LocalDateTime orderDate = order.getOpdate()
                        .toInstant()
                        .atZone(defaultZoneId)
                        .toLocalDateTime();
                Duration duration = Duration.between(orderDate, now);
                logger.debug("OrderDate===" + orderDate.toString()+",Now==="+now+",between==="+duration.getSeconds()+",setting time="+time);
                if (time < duration.getSeconds()) {
                    order.setCancelDate(Date.from(now.atZone(defaultZoneId).toInstant()));
                    order.setAppStatus(OrderAppStatus.CANCEL.getValue());
                    order.setOrderStatus(OrderStatus.CANCELED.getValue());
                    cancelList.add(order);
                }
            });
            if (CollectionUtils.isNotEmpty(cancelList)) {
                orderMapper.batchUpdateOrders(cancelList);
            }
        }
        logger.info("【Order Auto Cancel】 ===== End");
    }

    @Override
    @Transactional
    public void salesVolumeStatistics() {
        logger.info("【Sales Volume Statistics】 ===== Start");
        Calendar currentDate = Calendar.getInstance();
        deleteTodayStatistics(currentDate) ;
        insertStatistics(currentDate, currentDate) ;
        logger.info("【Sales Volume Statistics】 ===== End");
    }

    private void deleteTodayStatistics(Calendar currentDate){
        salesVolumeMapper.deleteByDate(DateUtils.convertDate2String(currentDate.getTime()));
        logger.info("【商品销量计算定时器】结束删除当天的统计记录");
        if (currentDate.get(Calendar.HOUR) == 0 && currentDate.get(Calendar.MINUTE) >= 1 && currentDate.get(Calendar.MINUTE) <= 11){
            //0:01 ~0:11重新统计前一天数据
            Calendar before = Calendar.getInstance();
            before.set(currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)-1,
                currentDate.get(Calendar.HOUR),currentDate.get(Calendar.MINUTE),currentDate.get(Calendar.SECOND));
            salesVolumeMapper.deleteByDate(DateUtils.convertDate2String(before.getTime()));
            logger.info("【商品销量计算定时器】结束删除前一天的统计记录");
            insertStatistics(before, currentDate) ;
            logger.info("【商品销量计算定时器】结束统计前一天的统计记录");
        }
    }

    private void insertStatistics(Calendar statisticsesDate, Calendar currentDate){
        LSalesVolume firstRecord = salesVolumeMapper.selectFirstRecord();
        if (firstRecord == null){
            List<SkuStatistics> skuStatisticses =  orderDetailMapper.selectSkuStatistics(null);
            insertStatisticsObj(skuStatisticses, currentDate.getTime());
        } else {
            List<SkuStatistics> skuStatisticses =  orderDetailMapper.selectSkuStatistics(DateUtils.convertDate2String(statisticsesDate.getTime()));
            insertStatisticsObj(skuStatisticses, currentDate.getTime());
        }
    }

    private void insertStatisticsObj(List<SkuStatistics> skuStatisticses, Date currentDate) {
        if (CollectionUtils.isNotEmpty(skuStatisticses)) {
            skuStatisticses.forEach(skuStatistics -> {
                LSalesVolume salesVolume = new LSalesVolume();
                salesVolume.setStatisticalDate(DateUtils.convertDate2String(skuStatistics.getOpdate()));
                salesVolume.setSkuId(skuStatistics.getSkuId());
                salesVolume.setRelatedSpuId(skuStatistics.getSpuId());
                salesVolume.setVolume(skuStatistics.getNum());
                salesVolume.setUpdateDate(currentDate);
                salesVolumeMapper.insert(salesVolume);
            });
        }
    }

    private int getStaticData(int value, String staticCode, String staticKey){
        //获取配置信息，若无，使用默认配置
        try {
            List<OfcRStaticData> staticDataList = staticDataService.getDatasByCodeAndKey(
                    staticCode, staticKey, null);
            if ( CollectionUtils.isNotEmpty(staticDataList)
                    && StringUtils.isNotBlank(staticDataList.get(0).getStaticValue()) ) {
                return Integer.parseInt(staticDataList.get(0).getStaticValue());
            }
        } catch ( Exception e ) {
            logger.error("获取默认配置出错", e);
        }
        return value;
    }
}
